import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class VendingMachineTest {
        VendingMachine myVendingMachine;
        VendingMachineItem snickers;
        VendingMachineItem coke;
        VendingMachineItem nonValidItem;
        
        @Before
        public void setUp() throws Exception {
                myVendingMachine = new VendingMachine();
                snickers = new VendingMachineItem("snickers", 1.25);
                coke = new VendingMachineItem("coke", 1.50);
        }

        @After
        public void tearDown() throws Exception {
        }

        // The item is valid and the slot is valid (slot A).
        @Test
        public void testAddItem_Case1a() {
                myVendingMachine.addItem(snickers, VendingMachine.A_CODE);
                
                assertEquals( snickers, myVendingMachine.getItem(VendingMachine.A_CODE) );
        }
        
        // The item is valid and the slot is valid (slot B).
        @Test
        public void testAddItem_Case1b() {
                myVendingMachine.addItem(snickers, VendingMachine.B_CODE);
                
                assertEquals( snickers, myVendingMachine.getItem(VendingMachine.B_CODE) );
        }
        
        // The item is valid and the slot is valid (slot C).
        @Test
        public void testAddItem_Case1c() {
                myVendingMachine.addItem(snickers, VendingMachine.C_CODE);
                
                assertEquals( snickers, myVendingMachine.getItem(VendingMachine.C_CODE) );
        }
        
        // The item is valid and the slot is valid (slot D).
        @Test
        public void testAddItem_Case1d() {
                myVendingMachine.addItem(snickers, VendingMachine.D_CODE);
                
                assertEquals( snickers, myVendingMachine.getItem(VendingMachine.D_CODE) );
        }
        
        // The item is valid, but the slot is invalid (non-empty slot, slot A).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case2a() {
                myVendingMachine.addItem(snickers, VendingMachine.A_CODE);
                myVendingMachine.addItem(coke, VendingMachine.A_CODE);
        }
        
        // The item is valid, but the slot is invalid (non-empty slot, slot B).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case2b() {
                myVendingMachine.addItem(snickers, VendingMachine.B_CODE);
                myVendingMachine.addItem(coke, VendingMachine.B_CODE);
        }
        
        // The item is valid, but the slot is invalid (non-empty slot, slot C).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case2c() {
                myVendingMachine.addItem(snickers, VendingMachine.C_CODE);
                myVendingMachine.addItem(coke, VendingMachine.C_CODE);
        }
        
        // The item is valid, but the slot is invalid (non-empty slot, slot D).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case2d() {
                myVendingMachine.addItem(snickers, VendingMachine.D_CODE);
                myVendingMachine.addItem(coke, VendingMachine.D_CODE);
        }
        
        // The item is valid, but the slot is invalid (non-existent code).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case3() {
                myVendingMachine.addItem(snickers, "E");
        }

        // The slot is valid (slot A), but the item is invalid.
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case4a() {
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.A_CODE);
        }
        
        // The slot is valid (slot B), but the item is invalid.
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case4b() {
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.B_CODE);
        }
        
        // The slot is valid (slot C), but the item is invalid.
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case4c() {
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.C_CODE);
        }
        
        // The slot is valid (slot D), but the item is invalid.
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case4d() {
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.D_CODE);
        }
        
        // The item is invalid and the slot is invalid (non-empty slot, slot A).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case5a() {
                myVendingMachine.addItem(snickers, VendingMachine.A_CODE);
                
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.A_CODE);
        }
        
        // The item is invalid and the slot is invalid (non-empty slot, slot B).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case5b() {
                myVendingMachine.addItem(snickers, VendingMachine.B_CODE);
                
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.B_CODE);
        }
        
        // The item is invalid and the slot is invalid (non-empty slot, slot C).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case5c() {
                myVendingMachine.addItem(snickers, VendingMachine.C_CODE);
                
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.C_CODE);
        }
        
        // The item is invalid and the slot is invalid (non-empty slot, slot D).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case5d() {
                myVendingMachine.addItem(snickers, VendingMachine.D_CODE);
                
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, VendingMachine.D_CODE);
        }
        
        // The item is invalid and the slot is invalid (non-existent code).
        @Test(expected = VendingMachineException.class)
        public void testAddItem_Case6() {
                nonValidItem = new VendingMachineItem("nonValidItem", -1.25);
                myVendingMachine.addItem(nonValidItem, "E");
        }
        
        // The slot is valid (slot A).
        @Test
        public void testRemoveItem_Case1a() {
                myVendingMachine.addItem(snickers, VendingMachine.A_CODE);
                
                assertEquals( snickers, myVendingMachine.removeItem(VendingMachine.A_CODE) );
        }
        
        // The slot is valid (slot B).
        @Test
        public void testRemoveItem_Case1b() {
                myVendingMachine.addItem(snickers, VendingMachine.B_CODE);
                
                assertEquals( snickers, myVendingMachine.removeItem(VendingMachine.B_CODE) );
        }
        
        // The slot is valid (slot C).
        @Test
        public void testRemoveItem_Case1c() {
                myVendingMachine.addItem(snickers, VendingMachine.C_CODE);
                
                assertEquals( snickers, myVendingMachine.removeItem(VendingMachine.C_CODE) );
        }
        
        // The slot is valid (slot D).
        @Test
        public void testRemoveItem_Case1d() {
                myVendingMachine.addItem(snickers, VendingMachine.D_CODE);
                
                assertEquals( snickers, myVendingMachine.removeItem(VendingMachine.D_CODE) );
        }

        // The slot is invalid (empty slot, slot A).
        @Test(expected = VendingMachineException.class)
        public void testRemoveItem_Case2a() {
                myVendingMachine.removeItem(VendingMachine.A_CODE);
        }
        
        // The slot is invalid (empty slot, slot B).
        @Test(expected = VendingMachineException.class)
        public void testRemoveItem_Case2b() {
                myVendingMachine.removeItem(VendingMachine.B_CODE);
        }
        
        // The slot is invalid (empty slot, slot C).
        @Test(expected = VendingMachineException.class)
        public void testRemoveItem_Case2c() {
                myVendingMachine.removeItem(VendingMachine.C_CODE);
        }
        
        // The slot is invalid (empty slot, slot D).
        @Test(expected = VendingMachineException.class)
        public void testRemoveItem_Case2d() {
                myVendingMachine.removeItem(VendingMachine.D_CODE);
        }
        
        // The slot is invalid (non-existent code).
        @Test(expected = VendingMachineException.class)
        public void testRemoveItem_Case3() {
                myVendingMachine.removeItem("E");
        }
        
        // The balance is valid.
        @Test
        public void testInsertMoney_Case1() {
                myVendingMachine.insertMoney(1.25);
                
                assertEquals ( 1.25, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is valid (=0).
        @Test
        public void testInsertMoney_Case2() {
                myVendingMachine.insertMoney(0.00);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is invalid (<0).
        @Test(expected = VendingMachineException.class)
        public void testInsertMoney_Case3() {
                myVendingMachine.insertMoney(-1.25);
        }

        // The balance is consistent, no change in machine state.
        @Test
        public void testGetBalance_Case1() {
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }

        // The balance is consistent when money is inserted once.
        @Test
        public void testGetBalance_Case2() {
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( 1.50, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent when money is inserted more than once.
        @Test
        public void testGetBalance_Case3() {
                myVendingMachine.insertMoney(1.00);
                myVendingMachine.insertMoney(.50);
                
                assertEquals ( 1.50, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot A) when money is insert once.
        @Test
        public void testGetBalance_Case4a() {
                myVendingMachine.addItem(coke, VendingMachine.A_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                myVendingMachine.makePurchase(VendingMachine.A_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot B) when money is insert once.
        @Test
        public void testGetBalance_Case4b() {
                myVendingMachine.addItem(coke, VendingMachine.B_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                myVendingMachine.makePurchase(VendingMachine.B_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot C) when money is insert once.
        @Test
        public void testGetBalance_Case4c() {
                myVendingMachine.addItem(coke, VendingMachine.C_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                myVendingMachine.makePurchase(VendingMachine.C_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot D) when money is insert once.
        @Test
        public void testGetBalance_Case4d() {
                myVendingMachine.addItem(coke, VendingMachine.D_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                myVendingMachine.makePurchase(VendingMachine.D_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot A) when money is inserted more than once.
        @Test
        public void testGetBalance_Case5a() {
                myVendingMachine.addItem(coke, VendingMachine.A_CODE);
                
                myVendingMachine.insertMoney(1.00);
                myVendingMachine.insertMoney(.50);
                
                myVendingMachine.makePurchase(VendingMachine.A_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot B) when money is inserted more than once.
        @Test
        public void testGetBalance_Case5b() {
                myVendingMachine.addItem(coke, VendingMachine.B_CODE);
                
                myVendingMachine.insertMoney(1.00);
                myVendingMachine.insertMoney(.50);
                
                myVendingMachine.makePurchase(VendingMachine.B_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot C) when money is inserted more than once.
        @Test
        public void testGetBalance_Case5c() {
                myVendingMachine.addItem(coke, VendingMachine.C_CODE);
                
                myVendingMachine.insertMoney(1.00);
                myVendingMachine.insertMoney(.50);
                
                myVendingMachine.makePurchase(VendingMachine.C_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is consistent after purchase (slot D) when money is inserted more than once.
        @Test
        public void testGetBalance_Case5d() {
                myVendingMachine.addItem(coke, VendingMachine.D_CODE);
        
                myVendingMachine.insertMoney(1.00);
                myVendingMachine.insertMoney(.50);
                
                myVendingMachine.makePurchase(VendingMachine.D_CODE);
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // The balance is valid and the slot is valid (slot A).
        @Test
        public void testMakePurchase_Case1a() {
                myVendingMachine.addItem(coke, VendingMachine.A_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( true, myVendingMachine.makePurchase(VendingMachine.A_CODE) );
        }
        
        // The balance is valid and the slot is valid (slot B).
        @Test
        public void testMakePurchase_Case1b() {
                myVendingMachine.addItem(coke, VendingMachine.B_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( true, myVendingMachine.makePurchase(VendingMachine.B_CODE) );
        }
        
        // The balance is valid and the slot is valid (slot C).
        @Test
        public void testMakePurchase_Case1c() {
                myVendingMachine.addItem(coke, VendingMachine.C_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( true, myVendingMachine.makePurchase(VendingMachine.C_CODE) );
        }
        
        // The balance is valid and the slot is valid (slot D).
        @Test
        public void testMakePurchase_Case1d() {
                myVendingMachine.addItem(coke, VendingMachine.D_CODE);
                
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( true, myVendingMachine.makePurchase(VendingMachine.D_CODE) );
        }
        
        // The balance is invalid (incorrect amount) and the slot is valid (slot A).
        @Test
        public void testMakePurchase_Case2a() {
                myVendingMachine.addItem(coke, VendingMachine.A_CODE);
                
                myVendingMachine.insertMoney(1.00);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.A_CODE) );
        }

        // The balance is invalid (incorrect amount) and the slot is valid (slot B).
        @Test
        public void testMakePurchase_Case2b() {
                myVendingMachine.addItem(coke, VendingMachine.B_CODE);
                
                myVendingMachine.insertMoney(1.00);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.B_CODE) );
        }
        
        // The balance is invalid (incorrect amount) and the slot is valid (slot C).
        @Test
        public void testMakePurchase_Case2c() {
                myVendingMachine.addItem(coke, VendingMachine.C_CODE);
                
                myVendingMachine.insertMoney(1.00);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.C_CODE) );
        }
        
        // The balance is invalid (incorrect amount) and the slot is valid (slot D).
        @Test
        public void testMakePurchase_Case2d() {
                myVendingMachine.addItem(coke, VendingMachine.D_CODE);
                
                myVendingMachine.insertMoney(1.00);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.D_CODE) );
        }
        
        // The balance is valid and the slot is invalid (empty slot, slot A).
        @Test
        public void testMakePurchase_Case3a() {
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.A_CODE) );
        }
        
        // The balance is valid and the slot is invalid (empty slot, slot B).
        @Test
        public void testMakePurchase_Case3b() {
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.B_CODE) );
        }
        
        // The balance is valid and the slot is invalid (empty slot, slot C).
        @Test
        public void testMakePurchase_Case3c() {
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.C_CODE) );
        }
        
        // The balance is valid and the slot is invalid (empty slot, slot D).
        @Test
        public void testMakePurchase_Case3d() {
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( false, myVendingMachine.makePurchase(VendingMachine.D_CODE) );
        }

        // Balance is >0 initially.
        @Test
        public void testReturnChange_Case1() {
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( 1.50, myVendingMachine.returnChange(), 0.0001 );
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // Balance is =0 initially.
        @Test
        public void testReturnChange_Case2() {
                assertEquals ( 0.00, myVendingMachine.returnChange(), 0.0001 );
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // Balance is <0 initially.
        @Test(expected = VendingMachineException.class)
        public void testReturnChange_Case3() {
                myVendingMachine.insertMoney(-1.50);
        }
        
        // Balance is set to 0.
        @Test
        public void testReturnChange_Case4() {
                myVendingMachine.returnChange();
                
                assertEquals ( 0.00, myVendingMachine.getBalance(), 0.0001 );
        }
        
        // Correct change is returned.
        @Test
        public void testReturnChange_Case5() {
                myVendingMachine.insertMoney(1.50);
                
                assertEquals ( 1.50, myVendingMachine.returnChange(), 0.0001 );
        }
}